package com.sikaryofficial.gateway.filter;

import cn.hutool.core.collection.CollUtil;
import com.sikaryofficial.common.core.constant.BrandEnum;
import com.sikaryofficial.common.core.constant.GatewayFilterOrdered;
import com.sikaryofficial.common.core.constant.SecurityConstants;
import com.sikaryofficial.common.core.secure.SecureComponent;
import com.sikaryofficial.common.core.secure.SecureComponentFactory;
import com.sikaryofficial.common.core.utils.ServletUtils;
import com.sikaryofficial.gateway.config.properties.RequestSignProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.PathContainer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.pattern.PathPatternParser;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.net.URI;
import java.util.List;
import java.util.Objects;

/**
 * 定制接口签名校验
 * <p>
 * 1、请求接口签名：防止暴力刷接口
 * <p>
 * 目前优先做 2
 *
 * @author qinjinyuan
 */
@Component
@ConditionalOnProperty(value = RequestSignProperties.RESPONSE_ENABLE, havingValue = "true")
@Slf4j
public class RequestSignFilter implements GlobalFilter, Ordered {
    /**
     * 算法
     */
    private SecureComponent secureComponent;
    @Resource
    private RequestSignProperties requestSignProperties;

    public SecureComponent getSecureComponent() {
        if (Objects.isNull(secureComponent)) {
            secureComponent = SecureComponentFactory.get(requestSignProperties.getAlgorithm());
        }
        return secureComponent;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 获取请求体
        ServerHttpRequest request = exchange.getRequest();
        String brand = exchange.getRequest().getHeaders().getFirst(SecurityConstants.BRAND_KEY);
        // 非主站则直接放行, 则直接放行
        if (!BrandEnum.HISMK.getCode().equalsIgnoreCase(brand)) {
            return chain.filter(exchange);
        }
        // 如果禁用签名校验, 则直接放行
        if (!requestSignProperties.enableDecryptRequestParam() || isNotNeedEncrypt(request.getURI())) {
            return chain.filter(exchange);
        }
        // 判定是否要进行签名认证
        if (isNeedEncrypt(request.getURI())) {
            // 分配的应用id
            String appId = exchange.getRequest().getHeaders().getFirst(SecurityConstants.APP_ID_KEY);
            // 时间戳
            String timestampStr = exchange.getRequest().getHeaders().getFirst(SecurityConstants.TIMESTAMP_KEY);
            //签名
            String signature = exchange.getRequest().getHeaders().getFirst(SecurityConstants.SIGNATURE_KEY);
            if (StringUtils.isBlank(appId) || StringUtils.isBlank(timestampStr) || StringUtils.isBlank(signature)) {
                log.error("sign param error. appId:{},timestampStr:{},signature:{}", appId, timestampStr, signature);
                return ServletUtils.webFluxResponseWriter(exchange.getResponse(), "sign param error");
            }
            // 拼接数据
            String origin = "appId=" + appId + ";timestamp=" + timestampStr;
            if (!getSecureComponent().verify(origin, signature)) {
                log.error("sign param error. appId:{},timestampStr:{},signature:{}", appId, timestampStr, signature);
                return ServletUtils.webFluxResponseWriter(exchange.getResponse(), "sign param error");
            }
            // 业务的时间戳
            long timestamp = Long.parseLong(timestampStr);
            // 时间差，配置10分钟
            long timeDifference = requestSignProperties.getTimeDifference() * 60 * 1000;
            if (Math.abs(timestamp - System.currentTimeMillis()) > timeDifference) {
                log.error("sign param error. appId:{},timestampStr:{},signature:{}", appId, timestampStr, signature);
                return ServletUtils.webFluxResponseWriter(exchange.getResponse(), "sign param error");
            }
        }
        return chain.filter(exchange);
    }

    /**
     * 是否需要加密
     *
     * @param uri
     * @return
     */
    private boolean isNeedEncrypt(URI uri) {
        List<String> encryptUriList = requestSignProperties.getEncryptUriList();
        if (CollUtil.isEmpty(encryptUriList)) {
            return false;
        }
        String path = uri.getPath();
        for (String pathPattern : encryptUriList) {
            if (isMatch(path, pathPattern)) {
                return true;
            }
        }
        return false;
    }

    private boolean isNotNeedEncrypt(URI uri) {
        List<String> encryptUriList = requestSignProperties.getNoEncryptUriList();
        if (CollUtil.isEmpty(encryptUriList)) {
            return false;
        }
        String path = uri.getPath();
        for (String pathPattern : encryptUriList) {
            if (isMatch(path, pathPattern)) {
                return true;
            }
        }
        return false;
    }

    private boolean isMatch(String path, String pathPattern) {
        // 这里需要根据whiteListed的格式来判断和匹配
        // 对于简单IP可以直接比较，对于范围和CIDR需要额外逻辑
        // ...
        return PathPatternParser.defaultInstance.parse(pathPattern).matches(PathContainer.parsePath(path));
    }

    @Override
    public int getOrder() {
        return GatewayFilterOrdered.REQUEST_SIGN;
    }

}
